Repository navigation
feat(samples): add 2D-to-3D Store Locator sample and seamless 2D/3D mode switching across showcase apps - #88
Merged
Merged
Conversation
LoyalAbbas
requested review from
dkhawk and
kikoso
and removed request for
dkhawk
October 19, 2026 15:51
dkhawk
reviewed
Oct 19, 2026
dkhawk
left a comment
Collaborator
There was a problem hiding this comment.
Review: 2D-to-3D Store Locator Sample & Mode Switching
Thank you for putting this together, @LoyalAbbas! The architecture and craftsmanship across this PR are fantastic:
- True 3-Way Parity: Achieving identical feature sets, styling, and kinematics across Kotlin Views, Java Views, and Jetpack Compose is a high benchmark and executed cleanly.
- MVVM Architecture: The domain separation in
ApiDemos/common(StoreLocationandStoreLocatorViewModel) providing immutableStateFlowalongsideasLiveData()for Java interoperability is well-structured and easy to follow. - Comprehensive ViewModel Testing: The 8 unit tests in
StoreLocatorViewModelTestthoroughly validate filtering, selection, and mode synchronization.
We tested the PR end-to-end on device (including millisecond logcat timing and autonomous UI exploration with Artemis). Here is our feedback and suggested refinements:
1. Map Readiness & 300ms Fallback Delay (SampleBaseActivity & GoogleMap3D)
- Observations: In
SampleBaseActivityandGoogleMap3D.kt, the readiness fallback delay was reduced from2000Lto300L. - Hardware Test Results: On cold launch testing on a Pixel device,
onMap3DViewReadyfired followed ~303ms later byonMapReady(viadelay(300)). The nativegoogleMap3D.setOnMapReadyListenernever fired during the session. The 300ms delay effectively worked around this SDK quirk, and the map surface, pins, and controls loaded cleanly behind the initial loading spinner. - Recommendation: Because
setOnMapReadyListeneris largely a no-op or one-shot inplay-services-maps3d:16.0.1, please add a brief code comment documenting why the 300ms fallback exists, so future maintainers understand it is an intentional workaround for the native callback.
2. Marker Re-Creation & Selection Performance in Views (StoreLocatorActivity)
- Location:
StoreLocatorActivity.kt(lines 281–352) &StoreLocatorActivity.java(lines 321–391) - The Issue: In
syncMapMarkersAndCamera:This conflates dataset changes (filtering amenities) with selection changes (user tapped a store card or pin).if (currentIds != lastRenderedStoreIds || selectedId != lastRenderedSelectedStoreId)
- Impact: Whenever any store is clicked,
rebuildStoreMarkersexecutesactiveMarkers.values.forEach { it.remove() }, clears the map, and invokesmap.addMarker(...)for all 8 stores from scratch. Destroying and re-creating 3D extruded pins causes noticeable pin flickering on the map and unnecessary native allocations at the exact moment the camera begins itsflyCameraToanimation. - Recommendation: Separate filtering from selection:
- Dataset changes (
currentIds != lastRenderedStoreIds): Diff and add/remove only the markers that entered or exited the filtered list. - Selection changes (
selectedId != lastRenderedSelectedStoreId): Selectively update only the previously selected marker (reverting scale to1.0f,zIndex = 1, and blue/gray color) and the newly selected marker (elevating scale to1.35f,zIndex = 10, and Google Yellow#F4B400). Keep the other 6 markers completely untouched. - Lifecycle: Add
onDestroy()overrides in bothStoreLocatorActivityclasses to explicitly removeactivePopoverandactiveMarkers.
- Dataset changes (
3. Product Identity: Default Mode in PlacesUIKit3D
- Location:
PlacesUIKit3D/src/main/java/com/example/placesuikit3d/ui/viewmodel/PlaceSearch3DViewModel.kt - The Issue: Changing the default launch state of
PlacesUIKit3Dfrom 3D Hybrid view to 2D Roadmap (tilt = 0.0). - Feedback: This module is our flagship showcase for
PlacesUIKit3D—demonstrating photorealistic 3D building meshes, orbital camera maneuvers, and 3D pins anchored to building roofs (AltitudeMode.RELATIVE_TO_MESH). Defaulting to a flat 2D roadmap hides the core 3D value proposition on first launch. Having a 2D switch is great, butPlacesUIKit3Dshould likely remain 3D by default on startup.
4. Duplicate Quick Settings Dialog in PlacesUIKit3D
- Location:
PlacesUIKit3D/src/main/java/com/example/placesuikit3d/MainActivity.kt - Feedback: The PR adds ~180 lines implementing an ad-hoc
AlertDialogwith RadioButtons and Switches for map modes, color schemes, and camera controls. This duplicates the exact layout and logic ofMap3DQuickSettingsWidget(Compose) andMap3DQuickSettingsDialog(Views). Maintaining two parallel copies of settings UI across sample modules creates tech debt when SDK control APIs evolve.
5. Scope & Granularity
- Feedback: This PR touches 56 files across 5 distinct modules (Store Locator across 3 frameworks, 2D/3D mode-switching in 5 existing samples,
PlacesUIKit3Dfeature work, andmaps3d-composelibrary changes). Consider splitting into:- PR 1: Canonical Store Locator sample (Kotlin, Java, Compose) and its core dependencies.
- PR 2: Cross-app 2D/3D mode-switching in existing showcase apps and
PlacesUIKit3D. - Benefit: Greatly simplifies review, minimizes blast radius, and preserves clean
git bisecthistory.
6. Android Lint & Code Polish
- GitHub Advanced Security / Lint:
Maps3DSamples/ApiDemos/common/src/main/res/values/strings.xml:325: Flagged spelling warning for"Drive-Thru". Please addtools:ignore="Spelling"if intentional.Maps3DSamples/ApiDemos/common/src/main/res/values/strings.xml:333: FlaggedPotential Pluralsfor"%1$d of %2$d stores". Please use<plurals>or addtools:ignore="PluralsCandidate".
- Hardcoded Strings:
- In
MainActivity.kt(Kotlin & Compose),text = "2D / ROADMAP"is hardcoded in Compose rather than referencing a string resource. - In
StoreLocation.kt,StoreAmenityhardcodes English label strings (val label: String) instead of string resource IDs (@StringRes val labelResId: Int). - In
PlacesRepositoryImpl.kt(line 441), category matching uses inline string regex heuristics (cleaned.contains("coffee"),"park", etc.). It would be cleaner to map standard Places API types (Place.Type) or use a structured enum map.
- In
- Redundant Startup Invocations:
- In
SampleBaseActivity.ktandSampleBaseActivity.java,onSampleEntered(),applyTo(), andgoogleMap3D.setCamera(initialCamera)are called duringonMap3DViewReadyand then executed again insideonMapReady. Consolidating these avoids duplicate camera snapping or interrupting flight animations on startup.
- In
dkhawk
enabled auto-merge (squash)
October 20, 2026 17:05
…ode switching across showcase apps
…nt warnings - Decouple dataset filtering reconciliation from marker selection state - Selectively update only previous and newly selected markers to prevent full marker teardown churn on store card tap - Add explicit onDestroy cleanup for active popovers and markers in Kotlin and Java StoreLocatorActivity - Resolve strings.xml lint warnings for 'Drive-Thru' spelling and plurals candidate
dkhawk
force-pushed
the
feat/2D-3D-Map-Samples
branch
from
October 20, 2026 19:05
0b2e4e9 to
8f83cc9
Compare
dkhawk
approved these changes
Oct 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Introduces a canonical Store Locator (2D & 3D) sample with 100% 3-way parity across Kotlin Views, Java Views, and Jetpack Compose, and adds seamless Switch to 2D / Switch to 3D mode transitions across all 2D/abstract-capable samples and
PlacesUIKit3D.Key Changes
1. Canonical Store Locator Sample (
StoreLocatorActivity&StoreLocatorViewModel)Maps3DSamples/ApiDemos/common):StoreLocationwith 8 curated San Francisco locations, Haversine distance sorting from Union Square, multi-amenity filtering (OPEN_NOW,DRIVE_THRU,OPEN_24_HOURS,EV_CHARGING), and dedicated 2D (tilt = 0.0°) and 3D (tilt = 55.0°) camera presets.StoreLocatorViewModelexposing immutableStoreLocatorUiStateviaStateFlow(for Kotlin/Compose) andLiveDataviaasLiveData()(for Java Views), preserving the active 2D/3D mode when selecting stores or filtering amenities.kotlin-app,java-app,ComposeDemos):1.35fon selection), and interactive 3DPopoverinfo cards.2D & 3Dcatalog badges inMainActivityacross all three sample apps forStoreLocatorActivity,RoutesActivity,RoadmapModeActivity,CloudStylingActivity, andDataVisualizationActivity.2. Seamless 2D ↔ 3D Mode Switching & Camera Perspective Alignment
StoreLocatorActivity,RoutesActivity,RoadmapModeActivity,CloudStylingActivity, andDataVisualizationActivityacross Kotlin, Java, and Compose, plus the top bar ofPlacesUIKit3D.Map3DMode.ROADMAPviews ontilt = 0.0°with zoomed-out city/neighborhood camera ranges (2200m–4500m) so roadmap mode renders as a clean 2D street map.altitude = 0.0) so toggling between 2D (tilt = 0.0°) and 3D (tilt = 45°–65°) pivots around the exact same geographic location without raycast drift.Map3DSettingsControllerbefore initial camera setup so landing on a sample in 3D mode immediately applies a 3D tilted camera without redundant startup reloads.Map3DStateInitial Sync: SeededlastSyncedCameraandlastSyncedMapModefromMap3DInitConfiginmaps3d-compose(GoogleMap3D/Map3DState) to prevent first-toggle recompositions from resetting activeflyCameraToanimations.3. Quick Settings Dialog Layout Polish
dialog_map3d_quick_settings.xmlandMap3DQuickSettingsDialog(Views) to pin the Reset to Defaults and Done action bar below a bounded<ScrollView>(layout_weight="1"), preventing bottom button clipping on smaller screens or immersive window insets.Map3DQuickSettingsWidget(Compose) and made full checkbox rows touch-toggleable.Testing
StoreLocatorViewModelTestcovering store selection in 2D and 3D modes, 2D/3D mode toggling, amenity filtering, and consumable camera flight commands.PlaceSearch3DViewModelTest,MainActivityTest(Kotlin), andMainActivityTest(Java)../gradlew spotlessCheckand all unit test suites across all modules.